package com.imooc.mall.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.imooc.mall.common.ResultInfo;
import com.imooc.mall.common.ResultInfoUtil;
import com.imooc.mall.dao.OrderItemMapper;
import com.imooc.mall.dao.OrderMapper;
import com.imooc.mall.dao.ProductMapper;
import com.imooc.mall.dao.ShippingMapper;
import com.imooc.mall.dto.OrderDto;
import com.imooc.mall.dto.OrderItemDto;
import com.imooc.mall.enums.PayTypeEnum;
import com.imooc.mall.enums.PaymentTypeEnum;
import com.imooc.mall.exception.MallException;
import com.imooc.mall.pojo.*;
import com.imooc.mall.service.ICartService;
import com.imooc.mall.service.IOrderService;
import com.sun.javafx.collections.MappingChange;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
@Slf4j
@SuppressWarnings("all")
public class OrderServiceImpl implements IOrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private OrderItemMapper orderItemMapper;

    @Autowired
    private ICartService cartService;

    @Autowired
    private ShippingMapper shippingMapper;

    @Autowired
    private ProductMapper productMapper;

    @Override
    @Transactional
    public ResultInfo<OrderDto> create(Integer uid, Integer shippingId) {
        //收货地址校验
        Map<String, Object> map = new HashMap<>();
        map.put("uid", uid);
        map.put("shippingId", shippingId);
        Shipping shipping = shippingMapper.selectByUidAndShippingId(map);
        if (null == shipping) {
            throw new MallException("收获地址不存在");
        }
        //获取购物车，校验商品，库存
        List<CartForRedis> cartListFromRedis = cartService.getCartListFromRedis(uid);
        List<CartForRedis> collect = cartListFromRedis.stream()
                .filter(CartForRedis::getProductSelected)
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(collect)) {
            throw new MallException("购物车无选中的商品");
        }
        /*for (CartForRedis cartListFromRedi : cartListFromRedis) {}*/
        //根据producId查数据库。为了避免在循环里操作数据库，需要优化，用in
        //优化方案：一次性将所有producId全查出,无重复用set
        Set<Integer> productIdSet = cartListFromRedis.stream().map(CartForRedis::getProductId).
                collect(Collectors.toSet());
        List<Product> productList = productMapper.findByProductIdSet(productIdSet);
        Map<Integer, Product> m = productList.stream().collect(Collectors.toMap(Product::getId, Function.identity()));
        /**
         * 企业级：分布式唯一id,这里只做简单生成
         */

        long orderNo = System.currentTimeMillis() + new Random().nextInt(999);
        List<OrderItem> orderItemList = new ArrayList<>();
        for (CartForRedis cartForRedis : collect) {
            Product product = m.get(cartForRedis.getProductId());
            //是否有商品
            if (null == product) {
                throw new MallException("商品不存在" + cartForRedis.getProductId());
            }
            //商品状态判断
            if (1 != product.getStatus()) {
                throw new MallException("商品不是在售状态:" + product.getName());
            }
            //判断库存
            if (product.getStock() < cartForRedis.getQuantity()) {
                throw new MallException("库存不正确+" + product.getName());
            }

            OrderItem orderItem = buildOrderItem(uid, orderNo, product, cartForRedis.getQuantity());
            orderItemList.add(orderItem);

            //减库存
            product.setStock(product.getStock() - cartForRedis.getQuantity());
            int i = productMapper.updateByPrimaryKey(product);
            if (i < 1) {
                log.error("扣减库存失败，productId==>{}", product.getId());
                throw new MallException("扣减库存失败:" + product.getName());
            }
        }
        //计算总价，只计算选中的商品
        BigDecimal bigDecimal = orderItemList.stream().
                map(OrderItem::getTotalPrice).
                reduce(BigDecimal.ZERO, BigDecimal::add);
        //生成订单，入库：order,order_item,事务
        Order order = createOrder(uid, orderNo, shippingId, orderItemList, bigDecimal);
        log.info("订单入库，orderId=>>{}", orderNo);
        int i = orderMapper.insertSelective(order);
        if (i < 1) {
            log.error("订单入库失败，orderId=>>{}", orderNo);
            throw new MallException("入库失败");
        }
        int j = orderItemMapper.batchInsert(orderItemList);
        if (j < 1) {
            log.error("订单详情入库失败，orderId=>>{}", orderNo);
            throw new MallException("入库失败");
        }
        //更新购物车，删掉选中的商品
        //redis也有事务（其实是打包命令），不能回滚
        for (CartForRedis cart : cartListFromRedis) {
            cartService.delete(uid, cart.getProductId());

        }
        //构造OrderDto返回前端
        OrderDto orderDto = buildOrderDto(order, orderItemList, shipping);
        return ResultInfoUtil.success(orderDto);
    }

    @Override
    public ResultInfo<PageInfo> list(Integer uid, Integer pageNum, Integer pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        List<Order> orderList = orderMapper.selectByUid(uid);
        Set<Long> orderIdSet = orderList.stream().map(Order::getOrderNo).
                collect(Collectors.toSet());
        List<OrderItem> orderItemList = orderItemMapper.selectByOrderNos(orderIdSet);

        Set<Integer> shippingIdSet = orderList.stream().map(Order::getShippingId).
                collect(Collectors.toSet());
        List<Shipping> shippingList = shippingMapper.selectByShippingIdSet(shippingIdSet);
        System.err.println("=============================================");
        //list转map  需掌握
        // key: orderNo value: List<OrderItem>
        Map<Long, List<OrderItem>> longListMap = orderItemList.stream().collect(Collectors.groupingBy(OrderItem::getOrderNo));
        //key:shippingId  value:Shipping
        Map<Integer, Shipping> shippingMap = shippingList.stream().collect(Collectors.toMap(Shipping::getId, Function.identity()));

        List<OrderDto> orderDtoList = new ArrayList<>();

        for (Order order : orderList) {
            OrderDto orderDto = buildOrderDto(order, longListMap.get(order.getOrderNo()), shippingMap.get(order.getShippingId()));
            orderDtoList.add(orderDto);
        }

        PageInfo pageInfo = new PageInfo(orderList);
        pageInfo.setList(orderDtoList);
        return ResultInfoUtil.success(pageInfo);
    }

    @Override
    public ResultInfo<OrderDto> detail(Integer uid, Long orderNo) {
        log.info("【查询订单详情】userId:{} orderNo:{}", uid, orderNo);
        Order order = orderMapper.selectByOrderNo(orderNo);
        if (null == order || !order.getUserId().equals(uid)) {
            throw new MallException("无此订单");
        }
        Set<Long> set = new HashSet<>();
        set.add(order.getOrderNo());
        List<OrderItem> orderItemList = orderItemMapper.selectByOrderNos(set);
        Shipping shipping = shippingMapper.selectByPrimaryKey(order.getShippingId());

        OrderDto orderDto = buildOrderDto(order, orderItemList, shipping);
        return ResultInfoUtil.success(orderDto);
    }

    @Override
    public ResultInfo cancel(Integer uid, Long orderNo) {
        Order order = orderMapper.selectByOrderNo(orderNo);
        if (null == order || !order.getUserId().equals(uid)) {
            throw new MallException("无此订单");
        }
        //只有未付款才可取消，自定义业务场景
        if (!order.getStatus().equals(PayTypeEnum.UN_PAY.getCode())){
            throw new MallException("订单状态有误");
        }
        order.setStatus(0);//最好建枚举
        order.setUpdateTime(new Date());
        order.setCloseTime(new Date());
        int i = orderMapper.updateByPrimaryKeySelective(order);
        if (i<1){
            log.error("取消订单有误==》》userId:{}orderNo:{}",uid,orderNo);
            throw new MallException(-1,"取消订单有误");
        }

        return ResultInfoUtil.success("取消成功");
    }

    @Override
    public void paid(Long orderNo) {
        Order order = orderMapper.selectByOrderNo(orderNo);
        if (null == order) {
            throw new MallException("无此订单：订单id==>>"+order.getId());
        }
        //只有未付款才可变成已付款，自定义业务场景
        if (!order.getStatus().equals(PayTypeEnum.UN_PAY.getCode())){
            throw new MallException("订单状态有误,订单id==>>"+order.getId());
        }
        order.setStatus(PayTypeEnum.PAID.getCode());
        order.setUpdateTime(new Date());
        order.setPaymentTime(new Date());
        int i = orderMapper.updateByPrimaryKeySelective(order);
        if (i<1){
            throw new MallException("将订单更新为已支付状态失败，订单id:"+order.getId());
        }
    }

    private OrderDto buildOrderDto(Order order, List<OrderItem> orderItemList, Shipping shipping) {
        OrderDto orderDto = new OrderDto();
        BeanUtils.copyProperties(order, orderDto);

        List<OrderItemDto> orderItemDtoList = orderItemList.stream().map(x -> {
            OrderItemDto orderItemDto = new OrderItemDto();
            BeanUtils.copyProperties(x, orderItemDto);
            return orderItemDto;
        }).collect(Collectors.toList());

        orderDto.setOrderItemDtoList(orderItemDtoList);
        if (null != shipping) {
            orderDto.setShippingId(shipping.getId());
            orderDto.setShippingDto(shipping);
        }

        return orderDto;

    }

    private Order createOrder(Integer uid, long orderNo, Integer shippingId, List<OrderItem> orderItemList, BigDecimal bigDecimal) {
        Order order = new Order();
        order.setUserId(uid);
        order.setOrderNo(orderNo);
        order.setShippingId(shippingId);
        order.setPayment(bigDecimal);
        order.setPaymentType(PaymentTypeEnum.ONLINE_PAY.getCode());
        order.setPostage(0);
        order.setStatus(PayTypeEnum.UN_PAY.getCode());
        order.setCreateTime(new Date());
        order.setUpdateTime(new Date());

        return order;

    }

    private OrderItem buildOrderItem(Integer uid, Long orderNo, Product product, Integer quantity) {
        OrderItem orderItem = new OrderItem();
        orderItem.setUserId(uid);
        orderItem.setOrderNo(orderNo);
        orderItem.setProductId(product.getId());
        orderItem.setProductImage(product.getMainImage());
        orderItem.setCurrentUnitPrice(product.getPrice());
        orderItem.setQuantity(quantity);
        orderItem.setTotalPrice(product.getPrice().multiply(BigDecimal.valueOf(quantity)));
        orderItem.setCreateTime(new Date());
        orderItem.setUpdateTime(new Date());

        return orderItem;
    }
}
